﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace Neoit.Utils.TemplateParser
{
    /// <summary>
    /// Parse If: 'age==18'
    /// </summary>
    public class ParseIf
    {
        //eg: <if $='age==18'></if>
        /// <summary>
        /// StartTagPattern
        /// </summary>
        public const string StartTagPattern = @"<if\s+\$='(.*?)'>";
        /// <summary>
        /// EndTagPattern
        /// </summary>
        public const string EndTagPattern = @"</if>";
        /// <summary>
        /// Parse
        /// </summary>
        public static string Parse<T>(string template, T data)
        {
            template = ParseTemplate(template, data);
            return template;
        }

        private static string ParseTemplate(string template, object data)
        {
            var stack = new Stack<Match>();
            var regex = new Regex($"{StartTagPattern}|{EndTagPattern}", RegexOptions.Singleline);
            var matches = regex.Matches(template);

            var deep = 0;
            foreach (Match match in matches)
            {
                if (match.Value != EndTagPattern)//startTag
                {
                    stack.Push(match);
                }
                else//endTag
                {
                    deep++;
                    stack.Pop();
                    if (stack.Count == 0)
                    {
                        var pattern = $"{StartTagPattern}({ParseCommon.JoinString(Enumerable.Repeat(EndTagPattern, deep - 1).Select(s => ".*?" + s))}.*?){EndTagPattern}";
                        var _match = Regex.Match(template, pattern, RegexOptions.Singleline);
                        var expression = _match.Groups[1];
                        var innerContent = _match.Groups[2].Value;

                        string replacement = EvaluateCondition(expression.Value, data) ? innerContent : string.Empty;
                        if (replacement == string.Empty && template.Substring(0, _match.Index).EndsWith("\r\n") && template.Substring(match.Index + match.Length).StartsWith("\r\n"))//直接删除行
                        {
                            template = template.Substring(0, _match.Index) + replacement + ParseCommon.TrimStart(template.Substring(match.Index + match.Length), "\r\n");
                        }
                        else
                        {
                            template = template.Substring(0, _match.Index) + replacement + template.Substring(match.Index + match.Length);
                        }

                        if (matches.ToList().IndexOf(match) < matches.Count - 1)
                        {
                            return ParseTemplate(template, data);
                        }
                        deep = 0;
                    }
                }
            }
            return template;
        }
        /// <summary>
        /// EvaluateCondition
        /// </summary>
        public static bool EvaluateCondition<T>(string condition, T data)
        {
            /*  bool type cases:
             *  list[0].age==90
             *  list[0].age>=90
             *  name == "90"
             *  name == name2
             *  isShow
             *  !isShow
             */
            var tempResult = false;
            condition = condition.Trim();

            // Handle negation (e.g., "!isShow")
            bool isNegation = condition.StartsWith("!");
            if (isNegation) condition = condition.Substring(1).Trim();

            if (IsBoolString(condition))
            {
                tempResult = Convert.ToBoolean(condition);
            }
            else
            {
                // Example: "age >= 18" is split into "age", ">=", "18"
                Match conditionMatch = Regex.Match(condition, @"(?<property>.+?)\s*(?<operator>[><=!]+)\s*(?<value>.+)");
                if (!conditionMatch.Success)
                {
                    return false;
                }

                string propertyName = conditionMatch.Groups["property"].Value;
                string @operator = conditionMatch.Groups["operator"].Value;
                var value = conditionMatch.Groups["value"].Value;

                var propertyValue = ParseCommon.GetValueByPath(data, propertyName);
                if (propertyValue == null) return false;

                if (value.StartsWith("\"") && value.EndsWith("\""))//string
                {
                    if (propertyValue.GetType() != typeof(string)) return false;
                    tempResult = @operator switch
                    {
                        "==" => value == Convert.ToString(propertyValue),
                        "!=" => value != Convert.ToString(propertyValue),
                        _ => false,
                    };

                }
                else if (ParseCommon.IsDecimal(value))//decimal
                {
                    if (!propertyValue.GetType().IsValueType) return false;
                    var _propertyValue = Convert.ToDecimal(propertyValue);
                    var _value = Convert.ToDecimal(value);
                    tempResult = @operator switch
                    {
                        ">=" => _propertyValue >= _value,
                        "<=" => _propertyValue <= _value,
                        ">" => _propertyValue > _value,
                        "<" => _propertyValue < _value,
                        "==" => _propertyValue == _value,
                        "!=" => _propertyValue != _value,
                        _ => false,
                    };
                }
                else if (Regex.IsMatch(value, @"\w+"))//variable
                {
                    var _value = ParseCommon.GetValueByPath(data, value);
                    if (_value == null || propertyValue == null)
                    {
                        tempResult = false;
                    }
                    else
                    {
                        tempResult = object.Equals(propertyValue, _value);
                    }
                }
                else if (value.ToLower() == "true" || value.ToLower() == "false")//bool
                {
                    tempResult = object.Equals(propertyValue, value);
                }
                else
                {

                }
            }


            return isNegation ? !tempResult : tempResult;
        }


        private static bool IsBoolString(string value)
        {
            if (value.ToLower() == "true" || value.ToLower() == "false") return true;
            return false;
        }
    }
}
